package main

import (
	"BLeetCode/help"
	"encoding/json"
	"strconv"

	//"encoding/json"
	"errors"
	"fmt"
	"github.com/360EntSecGroup-Skylar/excelize/v2"
	"github.com/huandu/xstrings"
	"github.com/jinzhu/copier"
	"reflect"
	"strings"
)

type Exceltest struct {
	A string
	B string
	C string
	D string
	E string
	F string
	G string
	H float64
	Xx
}
type Xx struct {
	X int
}
type KindSlice []reflect.Kind

//整数型kind集合
var IntKinds = KindSlice{
	reflect.Int,
	reflect.Int8,
	reflect.Int16,
	reflect.Int32,
	reflect.Int64,
	reflect.Uint,
	reflect.Uint8,
	reflect.Uint16,
	reflect.Uint32,
	reflect.Uint64,
}

//浮点kind集合
var FloatKinds = KindSlice{
	reflect.Float32,
	reflect.Float64,
}

func (k KindSlice) Contains(kind reflect.Kind) bool {
	for _, v := range k {
		if kind == v {
			return true
		}
	}
	return false
}

/**
filename excel文件名,
target 接收数据的结构体切片,
titleNum 标题开始行,
dataStart 数据开始行,
converPanic 数据转换错误是否panic
*/
func ImportExcel(filename string, target interface{}, titleNum, dataStart int, converPanic bool) (err error) {
	rv := reflect.ValueOf(target)
	if rv.Kind() != reflect.Ptr {
		return errors.New("target必需传入切片的指针")
	}
	rv = help.PtrValue(rv)
	if rv.Type().Kind() != reflect.Slice {
		return errors.New("target必需是切片")
	}
	tv := rv.Type()
	for tv.Kind() == reflect.Slice || tv.Kind() == reflect.Ptr {
		tv = tv.Elem()
	}
	if tv.Kind() != reflect.Struct {
		return errors.New("target必需是struct切片")
	}
	f, err := excelize.OpenFile(filename)
	if err != nil {
		fmt.Println(err)
		return err
	}
	// 获取 Sheet1 上所有单元格
	sheetName := f.GetSheetName(0)
	//所有行
	rows, err := f.GetRows(sheetName)
	if err != nil {
		return err
	}
	//标题行
	title := rows[titleNum]
	rowsNum := len(rows)
	//结构体字段与exceltitle的映射map
	fieldMap := makeFieldMap(title, tv)
	for i := dataStart; i < rowsNum; i++ {
		row := rows[i]
		//创建新结构体
		newv := reflect.New(tv)
		for fieldMap, colI := range fieldMap {
			fv := newv.Elem().FieldByName(fieldMap)
			if fv.IsValid() {
				//excel对应的字段值
				val := row[colI]
				//根据结构体字段类型转换并设置值
				if fv.Kind() == reflect.String {
					fv.SetString(val)
				} else if IntKinds.Contains(fv.Kind()) {
					iv, err := strconv.Atoi(val)
					//值错误
					if err != nil && converPanic {
						panic(err)
					}
					fv.SetInt(int64(iv))
				} else if FloatKinds.Contains(fv.Kind()) {
					iv, err := strconv.ParseFloat(val, 64)
					if err != nil && converPanic {
						panic(err)
					}
					fv.SetFloat(iv)
				}

			}
		}
		rv.Set(reflect.Append(rv, newv.Elem()))
	}

	//newv := reflect.New(tv)
	//newv.Elem().Field(0).SetString("HAHAHA")
	//tmp := reflect.Append(rv.Elem(), (newv.Elem()))
	//rv.Elem().Set(tmp)
	return err
}
func makeFieldMap(excelTitle []string, ty reflect.Type) map[string]int {
	fieldMap := make(map[string]int)
	for i := 0; i < ty.NumField(); i++ {
		tyField := ty.Field(i)
		if tyField.Anonymous {
			tmpMap := makeFieldMap(excelTitle, tyField.Type)
			copier.Copy(&fieldMap, tmpMap)
			continue
		}
		tmp := []string{
			tyField.Name,
			strings.ToLower(tyField.Name),
			strings.ToUpper(tyField.Name),
			xstrings.ToSnakeCase(tyField.Name),
			xstrings.ToCamelCase(tyField.Name),
			xstrings.ToKebabCase(tyField.Name),
		}
		for i2, s := range excelTitle {
			for _, s2 := range tmp {
				if s == s2 {
					fieldMap[tyField.Name] = i2
				}
			}
		}
	}
	return fieldMap
}

func main() {
	et := []Exceltest{}
	err := ImportExcel("D:\\test.xlsx", &et, 0, 1,true)
	fmt.Println(err)
	j, _ := json.Marshal(et)
	fmt.Println(string(j))
}
